import print from "./print";
import { Token, TokenType } from "./types";

export const tokenize = (
  template: string,
  data: { [k: string]: string | number }
): undefined | Token[] => {
  const tokens: Token[] = [];
  const regex = /(\d+\.?\d*|\.\d+)(e[+-]?\d+)?|([a-z]+)|([+\-*/^(),])/gi;

  if (!template || !data) {
    print.error("请输入计算模板以及参数");
    return undefined;
  }
  const FUNC_KEYS = Object.getOwnPropertyNames(Math).concat(["toFixeds"]);
  print.info("计算源模版:", template);
  print.info("计算源数据:", data);
  const tpls = template.replace(/(\b([a-zA-Z]+)\b)/g, (_, exp) => {
    if (FUNC_KEYS.includes(exp)) {
      return exp.trim();
    }
    const val = data[exp.trim()];
    if (val === undefined || val === null || val === "") {
      throw new Error(`data is error: '${exp}' value not find`);
    }
    return data[exp.trim()];
  });
  // 防止首位是负数，无法计算
  let prevChar = "operator";
  let match: RegExpExecArray | null;
  const symbols = ["+", "-", "*", "/", "%"];
  print.info("计算模版转化:", tpls);

  /***
   *@description 解析模版，注意负数
   */
  while ((match = regex.exec(tpls)) !== null) {
    const [, number, exponent, func, operator] = match;
    if (number) {
      const value =
        (prevChar === "-" ? prevChar : "") + number + (exponent || "");
      tokens.push({ type: "NUMBER", value });
      prevChar = "number";
    } else if (func) {
      tokens.push({ type: "FUNCTION", value: func.toLowerCase() });
      prevChar = "func";
    } else if (operator) {
      if (operator === "(" || operator === ")" || operator === ",") {
        tokens.push({ type: operator as TokenType });
        prevChar = operator;
      } else if (symbols.includes(operator) && prevChar !== "operator") {
        tokens.push({ type: operator as TokenType });
        prevChar = "operator";
      } else {
        prevChar = operator;
      }
    }
  }

  return tokens;
};
